package com.ncr.travel.appmgr.client.view;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.DOMException;
import com.google.inject.Inject;
import com.ncr.travel.appmgr.client.ConfigurationItem;
import com.ncr.travel.appmgr.client.mvp.EventBus;
import com.ncr.travel.appmgr.client.presenter.RulePresenter;
import com.ncr.travel.appmgr.client.ui.table.ColumnDescriptor;
import com.ncr.travel.appmgr.client.ui.table.LiveFlexTable;
import com.ncr.travel.appmgr.client.ui.table.TableData;
import com.ncr.travel.appmgr.client.ui.table.TableModel;
import com.ncr.travel.appmgr.client.ui.table.TableModelListener;
import com.ncr.travel.appmgr.model.shared.ColumnSpecification;
import com.ncr.travel.appmgr.model.shared.RowSpecification;
import com.ncr.travel.appmgr.model.shared.Rule;
import com.ncr.travel.appmgr.model.shared.RuleControls;
import com.ncr.travel.appmgr.model.shared.ScreenConfiguration;

/**
 * @author sn185078
 * 
 */
public class RuleWidget extends Composite implements RuleView, TableModelListener {

	@UiTemplate("RuleView.ui.xml")
	interface RuleUiBinder extends UiBinder<Widget, RuleWidget> {
	}

	private static RuleUiBinder uiBinder = GWT.create(RuleUiBinder.class);

	private RulePresenter presenter;

	private ConfigEditorView configEditor;

	@UiField
	HTMLPanel htmlPanel;
	@UiField
	VerticalPanel vPanel;
	@UiField
	ScrollPanel rulesScrollPanel;

	private String addButtonStyle = null;
	private String deleteButtonStyle = null;

	private List<Integer> deleteList = new ArrayList<Integer>();
	private Map<Integer, LiveFlexTable> flexTableMap;
	private TableModel<RuleData>[] tableModels;

	private String isAddRequired = "false";
	private String isDeleteRequired = "false";

	private ScreenConfiguration screenConfiguration;

	private ColumnDescriptor[] columnDescriptors;

	// This is the TableData implementation being used for the model; each row of the model will be represented by an instance of Rules
	private static class RuleData implements TableData {

		Map<String, Widget> map = new HashMap<String, Widget>();

		// Not supporting sorting in this example; probably better to throw an exception if your data won't support sorting
		public boolean compare(TableData obj, String column) {
			return false;
		}

		public Widget get(String column) {
			return map.get(column);
		}

		public void set(String column, Widget w) {
			map.put(column, w);
		}
	}

	/**
	 * Constructor
	 */
	@Inject
	public RuleWidget(EventBus eventBus) {
		initWidget(uiBinder.createAndBindUi(this));
		System.out.println("Constructing RuleWidget");
		this.configEditor = new ConfigEditorWidget("Are you sure you want to confirm rules configuration changes?", eventBus);
		configEditor.setScreenConfiguration(screenConfiguration);
		configEditor.setConfigurationItem(ConfigurationItem.RULE);
		htmlPanel.add(configEditor.asWidget());
	}

	public void setScreenConfiguration(final ScreenConfiguration screenConfiguration) {
		this.screenConfiguration = screenConfiguration;
		configEditor.setScreenConfiguration(screenConfiguration);
	}

	// Simply update the table display each time we're told that a cell has been updated in the model; modify DOM style just to easily see which int was modified
	public void modelCellUpdated(final TableModel<?> model, final int row, final int col) {
		DOM.setStyleAttribute(flexTableMap.get(model.hashCode()).getCellFormatter().getElement(row, col), "color", "#FC0000");
		flexTableMap.get(model.hashCode()).setWidget(row, col, model.getCell(row, col));
		Timer t = new Timer() {
			@Override
			public void run() {
				DOM.setStyleAttribute(flexTableMap.get(model.hashCode()).getCellFormatter().getElement(row, col), "color", "#000000");
			}
		};
		t.schedule(1000);
	}

	public void modelRowUpdated(TableModel<?> model, int row) {
		// Not being used in this example so no need to implement it
	}

	public void modelSorted(TableModel<?> model) {
		// Not being used in this example so no need to implement it
	}

	@Override
	public void showView() {

		vPanel.clear();
		configEditor.disableAllControls();
		
		List<Rule> rules = this.screenConfiguration.getRules();
		if(rules == null || rules.isEmpty()) {
		    return;
		}
		for (int ruleCount = 0; ruleCount < rules.size(); ruleCount++) {
			final Rule rule = rules.get(ruleCount);
			Label ruleTitle = new Label(rule.getRuleName());
			ruleTitle.setStyleName("confLabelRuleHeader");

			vPanel.add(ruleTitle);

			if (rule.getTableDefinition() != null) {
			    /*Number of tables*/
				int attributeSize = rule.getTableDefinition().size();

				flexTableMap = new HashMap<Integer, LiveFlexTable>();
				tableModels = new TableModel[attributeSize];
				// Window.alert(rule.getTableDefinition().size() + "");
				/*Iterate all tables*/
				for (int j = 0; j < attributeSize; j++) {
				    final int finalJ = j;
					// Create model 
				    /*Get Column Descriptor for the jth table*/
	                RuleData headerData = createColumnDescriptors(rule, j);
					TableModel<RuleData> model = new TableModel<RuleData>(columnDescriptors);
					/*Disable default label header*/
					model.setShowColumnDescriptorHeader(false);
					/*Add Header*/
					model.addRow(headerData);
//					model.addRow(createColumnDescriptors(rule, j));
					// Attach a listener to the model to receive events when the data in the model has been changed
					model.addListener(this);

					HorizontalPanel hPanel = new HorizontalPanel();
					if (rule.getTableDefinition().get(j).getColumnCount() > 0) {
						setRuleControls(rule, j);
						/*Iterate all rows*/
						for (int row = 0; row < rule.getTableDefinition().get(j).getRowSpecification().size(); row++) {
							RuleData data = new RuleData();
							RowSpecification rowSpec = rule.getTableDefinition().get(j).getRowSpecification().get(row);
							/*Create Row Widget*/
							data.set(columnDescriptors[0].getId(), presenter.createCellWidget(rowSpec.getRowValue(), rowSpec.getRowWidget(), false));
							if (rowSpec.getColSpecification() != null) {
								int colSize = rowSpec.getColSpecification().size();
								/*Iterate all columns in the row*/
								for (int col = 1; col < colSize + 1; col++) {
									ColumnSpecification colSpec = rowSpec.getColSpecification().get(col - 1);
									if (colSpec.getColumnValue() == null) {
										data.set(columnDescriptors[col].getId(), presenter.createCellWidget(rowSpec.getRowValue(), rowSpec.getRowWidget(), false));
									} else {
										data.set(columnDescriptors[col].getId(), presenter.createCellWidget(colSpec.getColumnValue(), colSpec.getColumnWidget(), false));
									}
								}
							}
							model.addRow(data);
						}
						final LiveFlexTable flexTable = new LiveFlexTable(model);
						flexTable.setStyleName("ruleFlexTable");
						tableModels[j] = model;
						flexTableMap.put(model.hashCode(), flexTable);
						if (isDeleteRequired.equals("true")) {
							Button deleteButton = new Button();
							deleteButton.setStyleName("deleteImageBtn");
							deleteButton.setText("Delete");
							deleteButton.addClickHandler(new ClickHandler() {
								public void onClick(ClickEvent event) {
									Comparator<Integer> comparator = Collections.reverseOrder();
									Collections.sort(deleteList, comparator);
									for (int index = 0; index < deleteList.size(); index++) {
										flexTable.removeRow(deleteList.get(index));
										configEditor.enablePreviewControl();
									}
									deleteList.clear();
								}
							});
							hPanel.add(deleteButton);
						}
						vPanel.add(hPanel);
						if (isAddRequired.equals("true")) {
						    Button addButton = new Button();
						    /*add is the second button*/
						    if(isDeleteRequired.equals("true")) {
						        addButton.setStyleName("addNewImageBtn");
						    } else {
						        /*should add to first position, use delete button style*/
						        addButton.setStyleName("deleteImageBtn");
						    }
						    addButton.setText("Add");
						    addButton.addClickHandler(new ClickHandler() {
						        public void onClick(ClickEvent event) {
						            configEditor.enablePreviewControl();
						            addRowToFlexTable(flexTable, createEmptyRow(rule, finalJ));
						        }
						    });
						    hPanel.add(addButton);
						}
						vPanel.add(flexTable);
						vPanel.add(new HTML("<hr/>"));
						vPanel.setStyleName("verticalPanel");
					}
				}
			}
		}
	}
	
	
	private void addRowToFlexTable(LiveFlexTable flexTable, RuleData ruleData) {
	    System.out.println(flexTable);
	    int row = flexTable.getRowCount();
	    for(int i = 0; i < ruleData.map.size(); i++) {
	        flexTable.setWidget(row, i, ruleData.get(i+""));
	    }
	    
	}

	private RuleData createColumnDescriptors(Rule rule, int j) {
	    /*Get first row specification*/
		RowSpecification rowSpec = rule.getTableDefinition().get(j).getRowSpecification().get(0);
		/*n column descriptor for n columns*/
		columnDescriptors = new ColumnDescriptor[rule.getTableDefinition().get(j).getColumnCount() + 1];
		ColumnDescriptor colDescFromRow = new ColumnDescriptor(0 + "", rowSpec.getRowName(), false);
		columnDescriptors[0] = colDescFromRow;
		RuleData data = null;
		if (rowSpec.getColSpecification() != null) {
		    data = new RuleData();
            data.set("" + 0, presenter.createCellWidget(rowSpec.getRowName(), rowSpec.getRowWidget(), true));
			int colSize = rowSpec.getColSpecification().size();// colArray.size();
			for (int col = 1; col < colSize + 1; col++) {
				ColumnSpecification colSpec = rowSpec.getColSpecification().get(col - 1);
				// Add ColumnDescriptors
				ColumnDescriptor colDesc = new ColumnDescriptor("" + col, colSpec.getColumnName(), false);
                columnDescriptors[col] = colDesc;
                if (colSpec.getColumnName() == null) {
                    data.set(columnDescriptors[col].getId(), presenter.createCellWidget(rowSpec.getRowName(), rowSpec.getRowWidget(), true));
                } else {
                    data.set(columnDescriptors[col].getId(), presenter.createCellWidget(colSpec.getColumnName(), colSpec.getColumnWidget(), true));
                }
           }
		}
		return data;
	}
	
	/**
	 * Create Empty Row
	 * @param Rule rule
	 * @param j jth table
	 */
	private RuleData createEmptyRow(Rule rule, int j) {
	       /*Get first row specification*/
        RowSpecification rowSpec = rule.getTableDefinition().get(j).getRowSpecification().get(0);
        /*n column descriptor for n columns*/
        RuleData data = null;
        if (rowSpec.getColSpecification() != null) {
            data = new RuleData();
            data.set("" + 0, presenter.createCellWidget("", rowSpec.getRowWidget(), false));
            int colSize = rowSpec.getColSpecification().size();// colArray.size();
            for (int col = 1; col < colSize + 1; col++) {
                ColumnSpecification colSpec = rowSpec.getColSpecification().get(col - 1);
                // Add ColumnDescriptors
                ColumnDescriptor colDesc = new ColumnDescriptor("" + col, colSpec.getColumnName(), false);
                if (colSpec.getColumnName() == null) {
                    data.set(colDesc.getId(), presenter.createCellWidget("", rowSpec.getRowWidget(), false));
                } else {
                    data.set(colDesc.getId(), presenter.createCellWidget("", colSpec.getColumnWidget(), false));
                }
           }
        }
        return data;
	}
	
	

	private void setRuleControls(Rule rule, int index) {

		if (rule.getRuleControls() == null)
			return;

		try {
			RuleControls ruleControls = rule.getRuleControls();

			// String ruleType = ruleControls.getRuleType();
			Boolean hasAddButton = ruleControls.getHasAddButton();
			Boolean hasDeleteButton = ruleControls.getHasDeleteButton();

			setIsDeleteRequired(hasDeleteButton.toString());
			setIsAddRequired(hasAddButton.toString());

		} catch (DOMException e) {
			System.out.println("Could not parse XML document.");
		}
	}

	@Override
	public final String getAddButtonStyle() {
		return addButtonStyle;
	}

	@Override
	public final ConfigEditorView getConfigEditor() {
		return configEditor;
	}

	@Override
	public final String getDeleteButtonStyle() {
		return deleteButtonStyle;
	}

	@Override
	public final TableModel<RuleData>[] getModels() {
		return tableModels;
	}

	@Override
	public final ColumnDescriptor[] getColumnDescriptors() {
		return columnDescriptors;
	}

	@Override
	public final TableModel<RuleData> getModel(int i) {
		return tableModels[i];
	}

	@Override
	public final ScrollPanel getFunctionScrollPanel() {
		return rulesScrollPanel;
	}

	@Override
	public final HasWidgets getHtmlPanel() {
		return htmlPanel;
	}

	@Override
	public final String getIsAddRequired() {
		return isAddRequired;
	}

	@Override
	public final String getIsDeleteRequired() {
		return isDeleteRequired;
	}

	@Override
	public final VerticalPanel getVerticalPanel() {
		return vPanel;
	}

	@Override
	public final void setAddButtonStyle(String addButtonStyle) {
		this.addButtonStyle = addButtonStyle;
	}

	@Override
	public final void setDeleteButtonStyle(String deleteButtonStyle) {
		this.deleteButtonStyle = deleteButtonStyle;
	}

	@Override
	public final void setIsAddRequired(String isAddRequired) {
		this.isAddRequired = isAddRequired;
	}

	@Override
	public final void setIsDeleteRequired(String isDeleteRequired) {
		this.isDeleteRequired = isDeleteRequired;
	}

	@Override
	public void setPresenter(RulePresenter presenter) {
		this.presenter = presenter;
	}

}
